@o scraps.c @#define SLAB_SIZE 500
typedef struct slab struct slab *next; char chars[SLAB_SIZE]; Slab; @| Slab SLAB_SIZE @
@o scraps.c @typedef struct char *file_name; int file_line; int page; char letter; Slab *slab; ScrapEntry; @| ScrapEntry @
@o scraps.c @static ScrapEntry *SCRAP[256];
#define scrap_array(i) SCRAP[(i) » 8][(i) & 255]
static int scraps; @| scraps scrap_array SCRAP @
@d Function pro... @extern void init_scraps(); extern int collect_scrap(); extern int write_scraps(); extern void write_scrap_ref(); extern void write_single_scrap_ref(); @
@o scraps.c @void init_scraps() scraps = 1; SCRAP[0] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry)); @| init_scraps @
@o scraps.c @void write_scrap_ref(file, num, first, page) FILE *file; int num; int first; int *page; if (scrap_array(num).page >= 0) if (first) fprintf(file, "else if (scrap_array(num).page != *page) fprintf(file, ", if (scrap_array(num).letter > 0) fputc(scrap_array(num).letter, file); else if (first) putc('?', file); else fputs(", ?", file); @<Warn (only once) about needing to rerun after Latex@> *page = scrap_array(num).page; @| write_scrap_ref @
@o scraps.c @void write_single_scrap_ref(file, num) FILE *file; int num; int page; write_scrap_ref(file, num, TRUE, &page); @| write_single_scrap_ref @
@d Warn (only once) about needing to... @ if (!already_warned) fprintf(stderr, "command_name); already_warned = TRUE; @
@d Global variable dec... @extern int already_warned; @| already_warned @
@d Global variable def... @int already_warned = 0; @
@o scraps.c @typedef struct Slab *scrap; Slab *prev; int index; Manager; @| Manager @
@o scraps.c @static void push(c, manager) char c; Manager *manager; Slab *scrap = manager->scrap; int index = manager->index; scrap->chars[index++] = c; if (index == SLAB_SIZE) Slab *new = (Slab *) arena_getmem(sizeof(Slab)); scrap->next = new; manager->scrap = new; index = 0; manager->index = index; @| push @
@o scraps.c @static void pushs(s, manager) char *s; Manager *manager; while (*s) push(*s++, manager); @| pushs @
@o scraps.c
@int collect_scrap()
Manager writer;
@<Create new scrap...@>
@<Accumulate scrap and return scraps++
@>
@| collect_scrap @
@d Create new scrap, managed by writer
@
Slab *scrap = (Slab *) arena_getmem(sizeof(Slab));
if ((scraps & 255) == 0)
SCRAP[scraps » 8] = (ScrapEntry *) arena_getmem(256 * sizeof(ScrapEntry));
scrap_array(scraps).slab = scrap;
scrap_array(scraps).file_name = save_string(source_name);
scrap_array(scraps).file_line = source_line;
scrap_array(scraps).page = -1;
scrap_array(scraps).letter = 0;
writer.scrap = scrap;
writer.index = 0;
@
@d Accumulate scrap... @ int c = source_get(); while (1) switch (c) case EOF: fprintf(stderr, "command_name, scrap_array(scraps).file_name, scrap_array(scraps).file_line); exit(-1); case '@@': @<Handle at-sign during scrap accumulation@> break; default: push(c, &writer); c = source_get(); break; @
@d Handle at-sign during scrap accumulation @ c = source_get(); switch (c) case '@@': pushs("@@@@", &writer); c = source_get(); break; case '|': @<Collect user-specified index entries@> case '': push('', &writer); return scraps++; case '<': @<Handle macro invocation in scrap@> break; default : fprintf(stderr, "command_name, c, source_name, source_line); exit(-1); @
@d Collect user-specified index entries @ do char new_name[100]; char *p = new_name; do c = source_get(); while (isspace(c)); if (c != '@@') Name *name; do *p++ = c; c = source_get(); while (c != '@@' && !isspace(c)); *p = ''; name = name_add(&user_names, new_name); if (!name->defs || name->defs->scrap != scraps) Scrap_Node *def = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node)); def->scrap = scraps; def->next = name->defs; name->defs = def; while (c != '@@'); c = source_get(); if (c != '') fprintf(stderr, "command_name, c, source_name, source_line); exit(-1); @
@d Handle macro invocation in scrap
@
Name *name = collect_scrap_name();
@<Save macro name@>
@<Add current scrap to name
's uses@>
c = source_get();
@
@d Save macro name @ char *s = name->spelling; int len = strlen(s) - 1; pushs("@@<", &writer); while (len > 0) push(*s++, &writer); len–; if (*s == ' ') pushs("...", &writer); else push(*s, &writer); pushs("@@>", &writer); @
@d Add current scrap to... @ if (!name->uses || name->uses->scrap != scraps) Scrap_Node *use = (Scrap_Node *) arena_getmem(sizeof(Scrap_Node)); use->scrap = scraps; use->next = name->uses; name->uses = use; @
@o scraps.c @static char pop(manager) Manager *manager; Slab *scrap = manager->scrap; int index = manager->index; char c = scrap->chars[index++]; if (index == SLAB_SIZE) manager->prev = scrap; manager->scrap = scrap->next; index = 0; manager->index = index; return c; @| pop @
@o scraps.c @static Name *pop_scrap_name(manager) Manager *manager; char name[100]; char *p = name; int c = pop(manager); while (TRUE) if (c == '@@') @<Check for end of scrap name and return@> else *p++ = c; c = pop(manager); @| pop_scrap_name @
@d Check for end of scrap name... @ c = pop(manager); if (c == '@@') *p++ = c; c = pop(manager); else if (c == '>') if (p - name > 3 && p[-1] == '.' && p[-2] == '.' && p[-3] == '.') p[-3] = ' '; p -= 2; *p = ''; return prefix_add(¯o_names, name); else fprintf(stderr, "exit(-1); @
@o scraps.c
@int write_scraps(file, defs, global_indent, indent_chars,
debug_flag, tab_flag, indent_flag)
FILE *file;
Scrap_Node *defs;
int global_indent;
char *indent_chars;
char debug_flag;
char tab_flag;
char indent_flag;
int indent = 0;
while (defs)
@<Copy defs->scrap
to file
@>
defs = defs->next;
return indent + global_indent;
@| write_scraps @
@d Copy defs->scrap... @{{ char c; Manager reader; int line_number = scrap_array(defs->scrap).file_line; @<Insert debugging information if required@> reader.scrap = scrap_array(defs->scrap).slab; reader.index = 0; c = pop(&reader); while (c) { switch (c) { case '@@': @<Check for macro invocation in scrap@> break; case '\n': putc(c, file); line_number++; @<Insert appropriate indentation@> break; case '\t': @<Handle tab...@> break; default: putc(c, file); indent_chars[global_indent + indent] = ' '; indent++; break; } c = pop(&reader); } }@} @d Insert debugging information if required @{if (debug_flag) { fprintf(file, "\n#line %d \"%s\"\n", line_number, scrap_array(defs->scrap).file_name); @<Insert appropr...@> }@} @d Insert approp... @{{ if (indent_flag) { if (tab_flag) for (indent=0; indent<global_indent; indent++) putc(' ', file); else for (indent=0; indent<global_indent; indent++) putc(indent_chars[indent], file); } indent = 0; }@} @d Handle tab characters on output @{{ if (tab_flag) @<Expand tab...@> else { putc('\t', file); indent_chars[global_indent + indent] = '\t'; indent++; } }@} @d Check for macro invocation... @{{ c = pop(&reader); switch (c) { case '@@': putc(c, file); indent_chars[global_indent + indent] = ' '; indent++; break; case '<': @<Copy macro into \verb
file|@>
@<Insert debugging information if required@>
break;
default: /* ignore, since we should already have a warning */
break;
@
@d Copy macro into... @ Name *name = pop_scrap_name(&reader); if (name->mark) fprintf(stderr, "command_name, name->spelling); exit(-1); if (name->defs) name->mark = TRUE; indent = write_scraps(file, name->defs, global_indent + indent, indent_chars, debug_flag, tab_flag, indent_flag); indent -= global_indent; name->mark = FALSE; else if (!tex_flag) fprintf(stderr, "command_name, name->spelling); @